/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// gl_warp.c -- sky and water polygons

#include "quakedef.h"

/*
ID3D10VertexShader *d3d_SkyWarpVS = NULL;
ID3D10VertexShader *d3d_SkyCubeVS = NULL;

ID3D10PixelShader *d3d_SkyWarpPS = NULL;
ID3D10PixelShader *d3d_SkyCubePS = NULL;

IDirect3DTexture9 *SolidSkyTexture = NULL;
IDirect3DTexture9 *AlphaSkyTexture = NULL;
IDirect3DCubeTexture9 *d3d_SkyCubemap = NULL;

IDirect3DVertexDeclaration9 *d3d_SkyVertexDeclaration = NULL;
*/

char skybox_name[128] = {0}; // name of current skybox, or "" if no skybox

QSHADER d3d_SkyWarpShader;
QSHADER d3d_SkyCubeShader;

QTEXTURE SolidSkyTexture;
QTEXTURE AlphaSkyTexture;

class CSkyHandler : public CD3DHandler
{
public:
	void OnCreateDevice (void)
	{
		d3d_SkyWarpShader.CreateShaders (IDR_SKYSHADER, "SkyWarpVS", NULL, "SkyWarpPS", INPUT_LAYOUT_SKY);
		d3d_SkyCubeShader.CreateShaders (IDR_SKYSHADER, "SkyCubeVS", NULL, "SkyCubePS", INPUT_LAYOUT_SKY);

		SolidSkyTexture.CreateTexture (NULL, 128, 128, TEX_NODISCARD);
		AlphaSkyTexture.CreateTexture (NULL, 128, 128, TEX_NODISCARD);
	}

	void OnReleaseDevice (void)
	{
		SolidSkyTexture.Release ();
		AlphaSkyTexture.Release ();

		Sky_ClearSkybox ();
	}

	void OnModeChange (void)
	{
		// bind the sky textures from slots 6 onwards so that they won't be overwritten and need only be bound once
		ID3D10ShaderResourceView *SkySRVs[] = {
			SolidSkyTexture.SRV,
			AlphaSkyTexture.SRV
		};

		d3d_Device->PSSetShaderResources (TEXTURE_SLOT_SOLIDSKY, 2, SkySRVs);
	}
} d3d_SkyHandler;


void R_DrawSkyChain (texturechain_t *SkyChain)
{
	if (0)//d3d_SkyCubemap && skybox_name[0] && !r_lightmap->value)
	{
		/*(
		d3d_State->PSSetSampler (2, d3d_MinMagFilter, D3DTEXF_NONE, D3DTADDRESS_CLAMP, d3d_Anisotropy);
		d3d_State->PSSetTexture (2, d3d_SkyCubemap);

		d3d_State->VSSetShader (d3d_SkyCubeVS);
		d3d_State->PSSetShader (d3d_SkyCubePS);
		*/
	}
	else
	{
		d3d_SkyWarpShader.Bind ();
	}

	d3d_State->OMSetDepthState (d3d_ZmodeNormal);
	d3d_State->OMSetBlendState (d3d_BlendNone);

	// and now draw it all
	R_DrawNewTextureChain (SkyChain, CHAIN_SKY);
}


/*
=============
R_InitSky

A sky texture is 256*128, with the right side being a masked overlay
==============
*/
void R_InitSky (miptex_t *mt, byte *texels)
{
	int			i, j;
	unsigned	transpix;

	// make an average value for the back to avoid a fringe on the top level
	int r = 0, g = 0, b = 0;

	unsigned *trans = NULL;
	QSCRATCHLOCKER Lock ((void **) &trans, __FUNCTION__);
	unsigned *solid = trans;
	unsigned *alpha = trans;

	for (i = 0; i < 128; i++)
	{
		for (j = 0; j < 128; j++)
		{
			int p = texels[i * 256 + j + 128];

			solid[j] = d_8to24table_rgba[p];

			r += ((byte *) &d_8to24table_rgba[p])[0];
			g += ((byte *) &d_8to24table_rgba[p])[1];
			b += ((byte *) &d_8to24table_rgba[p])[2];
		}

		solid += 128;
	}

	SolidSkyTexture.UpdateTexture ((byte *) trans, 128, 128, TEX_32BIT);

	((byte *) &transpix)[0] = r / (128 * 128);
	((byte *) &transpix)[1] = g / (128 * 128);
	((byte *) &transpix)[2] = b / (128 * 128);
	((byte *) &transpix)[3] = 0;

	for (i = 0; i < 128; i++)
	{
		for (j = 0; j < 128; j++)
		{
			int p = texels[i * 256 + j];

			if (p == 0)
				alpha[j] = transpix;
			else alpha[j] = d_8to24table_rgba[p];
		}

		alpha += 128;
	}

	AlphaSkyTexture.UpdateTexture ((byte *) trans, 128, 128, TEX_32BIT);
}


void Sky_LoadSkyBox (char *name)
{
	/*
	int i;
	int hunkmark;
	int maxextent = 0;

	imagedata_t skyface[6] = {{0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}};
	char *suf[6] = {"ft", "bk", "up", "dn", "rt", "lf"};

	if (!name || !name[0])
	{
		Sky_ClearSkybox ();
		return;
	}

	// if the name hasn't changed we can keep the old sky (provided we have one)
	if (!strcmp (skybox_name, name) && d3d_SkyCubemap) return;

	// clear the existing skybox as we're loading a new one
	Sky_ClearSkybox ();

	hunkmark = qScratch->GetLowMark ();

	for (i = 0; i < 6; i++)
	{
		if (Image_LoadImageFile (va ("gfx/env/%s%s", name, suf[i]), &skyface[i]) != NULL)
		{
			// and now we can find out how big the cubemap needs to be
			if (skyface[i].width > maxextent) maxextent = skyface[i].width;
			if (skyface[i].height > maxextent) maxextent = skyface[i].height;
		}
	}

	// if we got one then maxextent will be > 0
	// some mods don't provide a bottom-image
	if (maxextent > 0)
	{
		D3DSURFACE_DESC desc;

		// cache the name
		strcpy (skybox_name, name);

		// create a cube texture at the maximum extent of the images loaded, letting this handle crap like POT
		// and hardware max size limiting automatically for us
		if (SUCCEEDED (D3DXCreateCubeTexture (d3d_Device, maxextent, 1, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &d3d_SkyCubemap)))
		{
			if (SUCCEEDED (d3d_SkyCubemap->GetLevelDesc (0, &desc)))
			{
				for (i = 0; i < 6; i++)
				{
					IDirect3DSurface9 *Surf = NULL;

					// ensure that we got this face
					if (!skyface[i].data || !skyface[i].width || !skyface[i].height) continue;

					if (SUCCEEDED (d3d_SkyCubemap->GetCubeMapSurface ((D3DCUBEMAP_FACES) ((int) D3DCUBEMAP_FACE_POSITIVE_X + i), 0, &Surf)))
					{
						// set up params
						DWORD LoadFilter = D3DX_FILTER_NONE;
						RECT SrcRect = {0, 0, skyface[i].width, skyface[i].height};

						// switch the filter if the sizes don't match
						if (skyface[i].width != desc.Width || skyface[i].height != desc.Height)
							LoadFilter = D3DX_FILTER_LINEAR;

						// load it on
						D3DXLoadSurfaceFromMemory (Surf, NULL, NULL, skyface[i].data, D3DFMT_X8R8G8B8, skyface[i].width << 2, NULL, &SrcRect, LoadFilter, 0);

						// done
						Surf->Release ();
					}
				}
			}
			else Sky_ClearSkybox ();
		}
		else Sky_ClearSkybox ();
	}
	else Sky_ClearSkybox ();

	qScratch->FreeToLowMark (hunkmark);
	*/
}


void Sky_ClearSkybox (void)
{
	// called at map load and shutdown
//	SAFE_RELEASE (d3d_SkyCubemap);
	skybox_name[0] = 0;
}


/*
=================
Sky_NewMap
=================
*/
void Sky_NewMap (void)
{
	char	key[1024], value[4096];
	char	*data;

	// read worldspawn (this is so ugly, and shouldn't it be done on the server?)
	data = cl.worldmodel->entities;

	// FIXME: how could this possibly ever happen? -- if there's no
	// worldspawn then the sever wouldn't send the loadmap message to the client
	if (!data) return;
	if ((data = COM_Parse (data)) == NULL) return;
	if (com_token[0] != '{') return;

	while (1)
	{
		if ((data = COM_Parse (data)) == NULL) return;
		if (com_token[0] == '}') break;

		if (com_token[0] == '_')
			strcpy (key, com_token + 1);
		else strcpy (key, com_token);

		// remove trailing spaces
		while (key[strlen (key) - 1] == ' ')
			key[strlen (key) - 1] = 0;

		if ((data = COM_Parse (data)) == NULL) return;

		strcpy (value, com_token);

		if (!strcmp ("sky", key))
		{
			Sky_LoadSkyBox (value);
			return;
		}
		else if (!strcmp ("skyname", key))
		{
			// half-life
			Sky_LoadSkyBox (value);
			return;
		}
		else if (!strcmp ("qlsky", key))
		{
			// quake lives
			Sky_LoadSkyBox (value);
			return;
		}
	}

	// didn't load it so clear it down
	Sky_ClearSkybox ();
}


void Sky_LoadSkyBox_f (void)
{
	if (Cmd_Argc () != 2)
	{
		Con_Printf (PRINT_SAFE, "%s : loads a new skybox texture set\n", Cmd_Argv (0));
		return;
	}

	// this always clears down the skybox so that loadsky + an invalid arg will unload it
	Sky_ClearSkybox ();
	Sky_LoadSkyBox (Cmd_Argv (1));

//	if (d3d_SkyCubemap)
//		Con_Printf (PRINT_SAFE, "Loaded skybox %s OK\n", Cmd_Argv (1));
//	else Con_Printf (PRINT_SAFE, "Failed to load skybox %s OK\n", Cmd_Argv (1));
}

